﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;

using Newtonsoft.Json;

using Sustainalytics.FileManager;
using Sustainalytics.LongRunningTaskManager;
using Sustainalytics.LongRunningTaskManager.Interface;
using Sustainalytics.OAuth.Services.Common.Client;
using Sustainalytics.Portfolios.BL.Exceptions;
using Sustainalytics.Portfolios.Service.Models;
using Sustainalytics.Utils;


namespace Sustainalytics.Portfolios.Tasks
{
    public class PortfolioSecuritiesUploadTask : ILongRunningTask
    {
        private static EventLog _eventLog = null;

        private static PerformanceCounter _counter = null;

        public LongRunningTaskTypeEnum Type
        {
            get { return LongRunningTaskTypeEnum.PortfolioSecuritiesUpload; }
        }

        public object Start(LongRunningTaskContext context)
        {
            if (PortfolioSecuritiesUploadTask._eventLog == null)
            {
                PortfolioSecuritiesUploadTask._eventLog = new EventLog("Application");
                PortfolioSecuritiesUploadTask._eventLog.Source = "PortfolioSecuritiesUploadTask";
            }

            if (PortfolioSecuritiesUploadTask._counter == null)
            {
                PortfolioSecuritiesUploadTask._counter = new PerformanceCounter("Process", "% Processor Time", "Sustainalytics.Portfolios.Service", true);
            }


            // get upload data from parameter
            PortfolioUploadDto param = GetParameter(context.ParamJson);

            // get current LRTM host config (or any other means to obtain config)
            var taskManagerHostConfig = LongRunningTaskManagerHostConfiguration.Current;
            // create file manager
            var fileManager = new DiskFileManager(taskManagerHostConfig.DiskFileManagerBaseDirectory);

            // get files
            try
            {
                PortfolioSecuritiesUploadTask._eventLog.WriteEntry("OnStart - CPU usage:" + PortfolioSecuritiesUploadTask._counter.NextValue());

                // get file contents
                FileContents fileContents = fileManager.GetFile(param.FileManagerFilePaths[0]);

                PortfolioSecuritiesUploadTask._eventLog.WriteEntry("AfterFileContent - CPU usage:" + PortfolioSecuritiesUploadTask._counter.NextValue());

                // upload file along with its details and returns the new file
                ILongRunningTaskResult fileRes = UploadFileToPortfolioService(param, fileContents, context);

                PortfolioSecuritiesUploadTask._eventLog.WriteEntry("AfterUpload - CPU usage:" + PortfolioSecuritiesUploadTask._counter.NextValue());

                return fileRes;
            }
            catch (FileContentsNotFoundException)
            {
                context.AddError(5200, PortfolioSecuritiesUploadTask._eventLog, param.FileManagerFilePaths[0]);
            }
            catch (PortfolioSecuritiesInvalidFileException ex)
            {
                context.AddError(5201, PortfolioSecuritiesUploadTask._eventLog, ex.Errors);
            }
            catch (PortfolioManagementClaimNull)
            {
                context.AddError(5202, PortfolioSecuritiesUploadTask._eventLog);
            }
            catch (PortfolioUploadExcelFormatException ex)
            {
                context.AddError(5203, PortfolioSecuritiesUploadTask._eventLog);
            }
            return null;
        }

        private ILongRunningTaskResult UploadFileToPortfolioService(
            PortfolioUploadDto param,
            FileContents fileContents,
            LongRunningTaskContext context)
        {
            // create HTTP OAuth client
            OAuthClient oAuthClient = new OAuthClient(
                HostConfig.GetHostUrl() + HostConfig.GetSaveSecurityResource() + "/token",
                HostConfig.GetHostUrl() + HostConfig.GetSaveSecurityResource(),
                null,
                null);

            // set OAuth authentication ticket
            oAuthClient.SetAuthenticationTicket(context.Ticket);

            // create request data
            MultipartFormDataContent multipartContent = new MultipartFormDataContent();

            // add param to multipart content
            var paramContent = new ObjectContent<PortfolioUploadDto>(param, new JsonMediaTypeFormatter());
            multipartContent.Add(paramContent, "uploadParam");

            using (var fileStream = fileContents.StreamAccessor())
            {
                // add file contents to multipart content
                var fileHttpContent = new StreamContent(fileStream);
                fileHttpContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachement")
                {
                    FileName = RemoveIllegalCharacters.Remove(fileContents.Metadata.FileName)
                };
                fileHttpContent.Headers.ContentType = new MediaTypeHeaderValue(fileContents.Metadata.MimeType);
                multipartContent.Add(fileHttpContent, "uploadFile", RemoveIllegalCharacters.Remove(fileContents.Metadata.FileName));

                // POST
                HttpResponseMessage resp = oAuthClient.PostAsync(null, multipartContent, context.CancellationToken).Result;

                resp.EnsureSuccessStatusCode();

                // portfolio upload temporary folder
                var temporaryFolder = ConfigurationManager.AppSettings["portfolioUploadTempFolder"];

                if (string.IsNullOrEmpty(temporaryFolder))
                {
                    throw new NullReferenceException("Portfolio temporary download folder not defined in appSettings :: portfolioMatchDownloadTempFolder.");
                }

                // ensure that the temporary folder exists
                try
                {
                    if (Directory.Exists(temporaryFolder) == false)
                    {
                        Directory.CreateDirectory(temporaryFolder);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Format("Portfolio download temporary folder does not exist and couldn't be created. Path: {0}", temporaryFolder), ex);
                }

                // create multipart provider for obtaining content associated to multipart response content
                var multipartProvider = new MultipartFormDataStreamProvider(temporaryFolder);

                //retrieve data from multipart response
                resp.Content.ReadAsMultipartAsync(multipartProvider).Wait();

                // read portfolio file
                MultipartFileData portfolioMultipartFile = multipartProvider.FileData.FirstOrDefault();

                if (portfolioMultipartFile == null)
                {
                    throw new Exception("No multipart portfolio file data.");
                }

                // get portfolio details from FormData
                string detailsParam = multipartProvider.FormData["detailsParam"];
                string details = JsonConvert.DeserializeObject<string>(detailsParam);

                // get portfolio errors from FormData
                string errorsParam = multipartProvider.FormData["errorsParam"];
                string errors = JsonConvert.DeserializeObject<string>(errorsParam);

                if (errors != string.Empty)
                    throw new PortfolioUploadExcelFormatException(errors);

                string portfolioNameParam = multipartProvider.FormData["portfolioNameParam"];
                var portfolioName = JsonConvert.DeserializeObject<string>(portfolioNameParam);

                string filePath = portfolioMultipartFile.LocalFileName;
                string fileName = RemoveIllegalCharacters.Remove(portfolioMultipartFile.Headers.ContentDisposition.FileName);
                string mimeType = portfolioMultipartFile.Headers.ContentType.MediaType;

                byte[] resFileBytes = File.ReadAllBytes(filePath);

                var inMemFileRes = new LongRunningTaskFileInMemResult
                {
                    Contents = resFileBytes,
                    FileName = fileName,
                    MimeType = mimeType
                };

                context.TaskStatus.Details = details;
                context.TaskStatus.PortfolioName = portfolioName;

                return inMemFileRes;
            }
        }

        private PortfolioUploadDto GetParameter(string paramJson)
        {
            // check parameter
            if (string.IsNullOrEmpty(paramJson))
            {
                throw new PortfolioSecurityUploadParamException();
            }

            PortfolioUploadDto param = null;

            // check parameter
            try
            {
                param = JsonConvert.DeserializeObject<PortfolioUploadDto>(paramJson);
            }
            catch (Exception ex)
            {
                throw new PortfolioSecurityUploadParamException(null, ex);
            }

            // check parameter
            if (param.FileManagerFilePaths == null || param.FileManagerFilePaths.Length == 0)
            {
                throw new PortfolioSecurityUploadParamException();
            }
            return param;
        }
    }


    public static class LRTMContextExtension
    {
        public static void AddError(this LongRunningTaskContext context, int errorCode, EventLog log, string ex = null)
        {
            if (string.IsNullOrEmpty(ex))
            {
                context.TaskStatus.AddError(new LongRunningTaskError { ErrorCode = errorCode });
            }
            else
            {
                List<string> arg = new List<string> { ex };
                context.TaskStatus.AddError(new LongRunningTaskError { ErrorCode = 5201, Arguments = arg });
            }

            log.WriteEntry("Error " + errorCode + " Error: " + (string.IsNullOrEmpty(ex) ? "" : ex));
        }
    }
}
